// --------------------------------------------------------------------------------------------------------------------
// <copyright file="OrderService.SecurityChecker.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge
{
    #region Usings

    using System;
    using System.Reflection;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.Forge.DataCache;
    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Interfaces;

    #endregion

    /// <summary>
    /// The order service.
    /// </summary>
    public sealed partial class OrderService
    {
        /// <summary>
        /// The security checker.
        /// </summary>
        internal class SecurityChecker
        {
            #region Constants and Fields

            /// <summary>
            /// The identity.
            /// </summary>
            private static readonly Identity Identity = Identity.Create(MethodBase.GetCurrentMethod());

            #endregion

            #region Properties

            /// <summary>
            /// Gets or sets IdentityString.
            /// </summary>
            public static string IdentityString { get; set; }

            #endregion

            #region Public Methods

            /// <summary>
            /// The check order cancel request.
            /// </summary>
            /// <param name="order">
            /// The order.
            /// </param>
            /// <returns>
            /// </returns>
            public static Status CheckOrderCancelRequest(IOrder order)
            {
                Status status;

                // Check the order : null
                if (order == null)
                {
                    var message = string.Format("CheckOrderCancelRequest : Cannot allow send request, unknown order");
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, () => message);
                    goto done;
                }

                // Find the current data for given instrument
                var i = Singleton<DC>.Instance.GetInstrumentStaticDataFromCache(order.InstrumentID);

                // Check the instrument : Unknown
                if (i == null)
                {
                    var message = string.Format("CheckOrderCancelRequest : Cannot allow send request, unknown instrument with ID={0}", order.InstrumentID);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, () => message);
                    goto done;
                }

                // All is OK
                var smessage = string.Format(
                    "CheckOrderCancelRequest : Allowing send request , Instrument '{0}' ({1}) : Order ID = {2}", i.Code, i.TradingScope, order.ID);

                status = Status.Success(Identity, smessage);
                Logging.Log(Identity, ELogLevel.Info, () => smessage);

                // Filtering done
                goto done;

                done:
                Logging.Log(Identity, ELogLevel.Debug, () => status.DetailedMessage);
                return status;
            }

            /// <summary>
            /// he check order create request.
            /// </summary>
            /// <param name="instrumentID">
            /// </param>
            /// <param name="type">
            /// </param>
            /// <param name="way">
            /// </param>
            /// <param name="price">
            /// </param>
            /// <param name="quantity">
            /// </param>
            /// <param name="bookingPath">
            /// </param>
            /// <returns>
            /// </returns>
            public static Status CheckOrderCreateRequest(Guid instrumentID, EOrderType type, EOrderWay way, double price, int quantity, string bookingPath)
            {
                var v1 = Timings.BenchmarkNow;
                Status status;

                // Find the current data for given instrument
                var i = Singleton<DC>.Instance.GetInstrumentStaticDataFromCache(instrumentID);

                // Check the instrument : Unknown
                if (i == null)
                {
                    var message = string.Format("CheckOrderCreateRequest : Cannot authorize order creation, instrument with ID={0} is not known", instrumentID);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check Quantity
                if (quantity <= 0)
                {
                    var message = string.Format(
                        "CheckOrderCreateRequest : Cannot allow send request, Instrument '{0}' ({1}) : Invalid Quantity", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check Price
                if (price < Constants.Zero)
                {
                    var message = string.Format(
                        "CheckOrderCreateRequest : Cannot allow send request, Instrument '{0}' ({1}) : Invalid Price", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check Way
                if (way == 0)
                {
                    var message = string.Format(
                        "CheckOrderCreateRequest : Cannot allow send request, Instrument '{0}' ({1}) : Invalid Order Way", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check Order Type
                if (type == 0)
                {
                    var message = string.Format(
                        "CheckOrderCreateRequest : Cannot allow send request, Instrument '{0}' ({1}) : Invalid Order Type", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check Booking Path
                if (bookingPath.Length == 0)
                {
                    var message = string.Format(
                        "CheckOrderCreateRequest : Cannot allow send request, Instrument '{0}' ({1}) : Invalid Booking Path", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // All is ok
                var smessage = string.Format(
                    "CheckOrderCreateRequest : Authorizing order creation Instrument '{0}' ({1}) : Quantity={2} Price={3}", 
                    i.Code, 
                    i.TradingScope, 
                    quantity, 
                    price);
                status = Status.Success(Identity, smessage);
                Logging.Log(Identity, ELogLevel.Info, status);

                done:
                Timings.Benchmark("CheckOrderCreateRequest - before logging", v1);
                Logging.Log(Identity, ELogLevel.Debug, () => status.DetailedMessage);
                Timings.Benchmark("CheckOrderCreateRequest - logging done", v1);
                return status;
            }

            /// <summary>
            /// The check order modify request.
            /// </summary>
            /// <param name="oldOrder">
            /// The oldOrder.
            /// </param>
            /// <param name="newOrder">
            /// The newOrder.
            /// </param>
            /// <returns>
            /// </returns>
            public static Status CheckOrderModifyRequest(IOrder oldOrder, IOrder newOrder)
            {
                Status status;

                // Check the old order : null
                if (oldOrder == null)
                {
                    var message = string.Format("CheckOrderModifyRequest : Cannot allow send request, Unknown Old Order");
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check the new order : null
                if (newOrder == null)
                {
                    var message = string.Format("CheckOrderModifyRequest : Cannot allow send request, Unknown New Order");
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check the old order : IsValid
                if (!oldOrder.IsValid)
                {
                    var message = string.Format("CheckOrderModifyRequest : Cannot allow send request, Invalid Old Order ID={0}", oldOrder.ID);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check the new order : IsValid
                if (!newOrder.IsValid)
                {
                    var message = string.Format("CheckOrderModifyRequest : Cannot allow send request, Invalid New Order ID={0}", newOrder.ID);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check Order Instrument
                if (oldOrder.InstrumentID != newOrder.InstrumentID)
                {
                    var message =
                        string.Format(
                            "CheckOrderModifyRequest : Cannot allow send request, Not the same InstrumentID, Old Order : {0} ({1}) - New Order : {2} ({3})", 
                            oldOrder.ID, 
                            oldOrder.InstrumentID, 
                            newOrder.ID, 
                            newOrder.InstrumentID);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check Order Type
                if (oldOrder.Type != newOrder.Type)
                {
                    var message =
                        string.Format(
                            "CheckOrderModifyRequest : Cannot allow send request, Not the same Order Type, Old Order : {0} ({1})- New Order : {2} ({3})", 
                            oldOrder.ID, 
                            oldOrder.Type, 
                            newOrder.ID, 
                            newOrder.Type);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check Order Way
                if (oldOrder.Way != newOrder.Way)
                {
                    var message =
                        string.Format(
                            "CheckOrderModifyRequest : Cannot allow send request, Not the same Order Way, Old Order : {0} ({1})- New Order : {2} ({3})", 
                            oldOrder.ID, 
                            oldOrder.Way, 
                            newOrder.ID, 
                            newOrder.Way);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Find the current data for given instrument
                var i = Singleton<DC>.Instance.GetInstrumentStaticDataFromCache(newOrder.InstrumentID);

                // Check the instrument : Unknown
                if (i == null)
                {
                    var message = string.Format("CheckOrderModifyRequest : Cannot allow send request, Unknown instrument with ID={0}", newOrder.InstrumentID);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is enabled
                if (!i.Enabled)
                {
                    var message = string.Format(
                        "CheckOrderModifyRequest : Cannot allow send request, instrument '{0}' ({1}) is not enabled", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is enabled to be bought
                if (newOrder.Way == EOrderWay.Buy && !i.BuyEnabled)
                {
                    var message = string.Format(
                        "CheckOrderModifyRequest : Cannot allow send request, instrument '{0}' ({1}) is not buy enabled", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is enabled to be sold
                if (newOrder.Way == EOrderWay.Sell && !i.SellEnabled)
                {
                    var message = string.Format(
                        "CheckOrderModifyRequest : Cannot allow send request, instrument '{0}' ({1}) is not sel enabled", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is not restricted
                if (i.Restricted)
                {
                    var message = string.Format(
                        "CheckOrderModifyRequest : Cannot allow send request, instrument '{0}' ({1}) is restricted", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is not BlackListed
                if (i.BlackListed)
                {
                    var message = string.Format(
                        "CheckOrderModifyRequest : Cannot allow send request, instrument '{0}' ({1}) is blacklisted", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // All is OK
                var smessage =
                    string.Format(
                        "CheckOrderModifyRequest : Allowing send request , Instrument '{0}' ({1}) : Old Order = {2} @ {3} - New Order = {4} @ {5}", 
                        i.Code, 
                        i.TradingScope, 
                        oldOrder.Quantity, 
                        oldOrder.Price, 
                        newOrder.Quantity, 
                        newOrder.Price);
                status = Status.Success(Identity, smessage);
                Logging.Log(Identity, ELogLevel.Info, status);

                // Filtering done
                goto done;

                done:
                Logging.Log(Identity, ELogLevel.Debug, status);
                return status;
            }

            /// <summary>
            /// Check "Send" of an order (security rules filter)
            /// </summary>
            /// <param name="order">
            /// </param>
            /// <returns>
            /// </returns>
            public static Status CheckOrderSendRequest(IOrder order)
            {
                // Benchmark
                var v1 = Timings.BenchmarkNow;
                Status status;

                // Check the order

                // Check the order : null
                if (order == null)
                {
                    var message = string.Format("CheckOrderSendRequest: Cannot allow send request, unknown order");
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check the order : invalid order
                if (!order.IsValid)
                {
                    var message = string.Format("CheckOrderSendRequest: Cannot allow send request, invalid order ID={0}", order.ID);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check the instrument

                // Find the current data for given instrument
                var i = Singleton<DC>.Instance.GetInstrumentStaticDataFromCache(order.InstrumentID);

                // Check the instrument : Unknown
                if (i == null)
                {
                    var message = string.Format("CheckOrderSendRequest: Cannot allow send request, unknown instrument with ID={0}", order.InstrumentID);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is enabled
                if (!i.Enabled)
                {
                    var message = string.Format(
                        "CheckOrderSendRequest: Cannot allow send request, instrument '{0}' ({1}) is not enabled", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is enabled to be be bought
                if (order.Way == EOrderWay.Buy && !i.BuyEnabled)
                {
                    var message = string.Format(
                        "CheckOrderSendRequest: Cannot allow send request, instrument '{0}' ({1}) is not buy enabled", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is enabled to be be sell
                if (order.Way == EOrderWay.Sell && !i.SellEnabled)
                {
                    var message = string.Format(
                        "CheckOrderSendRequest: Cannot allow send request, instrument '{0}' ({1}) is not sel enabled", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is not restricted
                if (i.Restricted)
                {
                    var message = string.Format(
                        "CheckOrderSendRequest: Cannot allow send request, instrument '{0}' ({1}) is restricted", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // Check if the instrument is not BlackListed
                if (i.BlackListed)
                {
                    var message = string.Format(
                        "CheckOrderSendRequest: Cannot allow send request, instrument '{0}' ({1}) is blacklisted", i.Code, i.TradingScope);
                    status = Status.Failure(Identity, message);
                    Logging.Log(Identity, ELogLevel.Error, status);
                    goto done;
                }

                // All is OK
                var smessage = string.Format(
                    "CheckOrderSendRequest: Authorizing send request '{0}' ({1}) : Quantity={2} Price={3}", i.Code, i.TradingScope, order.Quantity, order.Price);
                status = Status.Success(Identity, smessage);
                Logging.Log(Identity, ELogLevel.Info, status);

                done:
                Timings.Benchmark("CheckOrderSendRequest - before logging", v1);
                Logging.Log(Identity, ELogLevel.Debug, () => status.DetailedMessage);
                Timings.Benchmark("CheckOrderSendRequest - logging done", v1);

                // All done
                return status;
            }

            #endregion
        }
    }
}